package net.kehuilai;

import com.alibaba.fastjson.JSON;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import net.kehuilai.handler.MessageDecoderHandler;
import net.kehuilai.handler.MessageEncoderHandler;
import net.kehuilai.handler.ServerHandler;
import net.kehuilai.model.BaseMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by genghz on 16/12/6.
 */
public class SocketServer {
    private static final Logger log = LoggerFactory.getLogger(SocketServer.class);

    private final String host;
    private final int port;

    private final ServerHandler handler;

    private NioEventLoopGroup bossGroup;
    private NioEventLoopGroup workerGroup;

    public SocketServer(String host, int port) {
        this.host = host;
        this.port = port;
        this.handler = new ServerHandler();
    }

    public void start() {

        final SocketServer me = this;

        this.bossGroup = new NioEventLoopGroup();
        this.workerGroup = new NioEventLoopGroup(1);

        ServerBootstrap boot = new ServerBootstrap();
        try {
            boot.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .option(ChannelOption.SO_REUSEADDR, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            // 设置拦截处理栈
                            channel.pipeline()
                                    .addLast("packer", new LengthFieldPrepender(4))   // 自动设置消息长度
                                    .addLast("unpacker", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))   // 自动过滤消息长度
                                    .addLast("encoder", new MessageEncoderHandler())
                                    .addLast("decoder", new MessageDecoderHandler())
                                    .addLast("handler", handler);
                        }
                    });

            ChannelFuture future = boot
                    .bind(host, port)
                    .addListener(new ChannelFutureListener() {
                        public void operationComplete(ChannelFuture future) throws Exception {
                            if (future.isSuccess()) {
                                log.info("socket server started at port: {}", port);
                            } else {
                                log.error("socket server start failed at port: {}!", port);
                            }
                        }
                    });

            future.syncUninterruptibly();
        } catch (Exception e) {
            this.close();
        }
    }

    public void close() {
        this.bossGroup.shutdownGracefully().syncUninterruptibly();
        this.workerGroup.shutdownGracefully().syncUninterruptibly();
    }

    public void send(String clientId, String method, Object msg) {
        Channel channel = handler.getClient(clientId);
        if (channel == null || !channel.isActive()) {
            log.error("not found client connect: {}", clientId);
            return;
        }
        BaseMessage message = new BaseMessage(method, msg.getClass().getName(), JSON.toJSONString(msg));
        channel.writeAndFlush(message);
    }
}
